//
//  BDMBaseOperationManager.m
//  welkin
//
//  Created by liyingpeng on 16/2/22.
//  Copyright © 2016年 liyingpeng. All rights reserved.
//

#import "BDMBaseOperationManager.h"
#import "AFNetworking.h"
#import "UIKit+AFNetworking.h"
#import "SDURLCache.h"
#import "BDMError.h"
#import "UIDevice+serialNumber.h"
#import <libkern/OSAtomic.h>

#define kCacheSizeMemory    4*1024*1024     // 4MB
#define kCacheSizeDisk      100*1024*1024   // 100MB

@interface BDMBaseOperationManager ()

@property(nonatomic, strong) NSMutableDictionary *dispatchTable;
@property(nonatomic, strong) NSMutableArray *groupRequestArray;

@end

@implementation BDMBaseOperationManager

#pragma mark - life circle

- (instancetype)initWithBaseURL:(NSURL *)url {
    if (self = [super initWithBaseURL:url]) {
    }
    return self;
}

#pragma mark - public method

- (void)urlCacheInitialization
{
    SDURLCache *urlCache = [[SDURLCache alloc] initWithMemoryCapacity:kCacheSizeMemory
                                                         diskCapacity:kCacheSizeDisk
                                                             diskPath:nil];
    [NSURLCache setSharedURLCache:urlCache];
    
    [[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
}

- (void)clearAllURLCache
{
    [[NSURLCache sharedURLCache] removeAllCachedResponses];
}

-(void)sendHttpRequestWithModel:(BDMHTTPRequestModel *) aRequestModel
                        success:(void (^)(id responseObject))aSuccessBlock
                        failure:(void (^)(BDMError *error))aFailureBlock
{
    //避免同一请求重复请求
    if ([BDMUtility isStringValidated:aRequestModel.identifier] && [self.dispatchTable objectForKey:aRequestModel.identifier] ) {
        return;
    }
    
    if (self.configRequestModelBlock) {
        aRequestModel = self.configRequestModelBlock(aRequestModel);
    }
    // 通过https的所有请求,因https请求需要证书
    self.securityPolicy.allowInvalidCertificates = !aRequestModel.isUseSSL;

    id parameters = aRequestModel.paraDict;
    
    if ([aRequestModel.requestMethod isEqualToString:HTTP_REQUEST_POST]) {
        if (HTTP_REQUEST_TYPE_JSON == aRequestModel.requestType) {
            parameters = aRequestModel.postDict;
            self.requestSerializer = [AFJSONRequestSerializer serializer];
        } else if (HTTP_REQUEST_TYPE_DATA_UCLOGIN == aRequestModel.requestType) {
            parameters = [aRequestModel.postDict objectForKey:HTTP_REQUEST_POST];
            self.requestSerializer = [AFUCLoginRequestSerializer serializer];
        } else if (HTTP_REQUEST_TYPE_FORM == aRequestModel.requestType) {
            self.requestSerializer = [AFHTTPRequestSerializer serializer];
            parameters = aRequestModel.postDict;
        }
    }
    else {
        self.requestSerializer = [AFJSONRequestSerializer serializer];
    }
    
    if (HTTP_RETURN_TYPE_JSON == aRequestModel.returnType) {
        self.responseSerializer = [AFJSONResponseSerializer serializer];
    } else if (HTTP_RETURN_TYPE_DATA == aRequestModel.returnType){
        self.responseSerializer = [AFHTTPResponseSerializer serializer];
    }
    
    AFHTTPRequestOperation *currentOperation = [self URL:aRequestModel.requestUrl
                                           requestMethod:aRequestModel.requestMethod
                                          customerHeader:aRequestModel.customHeader
                                                 paraDic:aRequestModel.paraDict
                                                 parameters:parameters
                                             cachePolicy:aRequestModel.cachePolicy
                                                 success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                                     //删除网络请求
                                                     if ([BDMUtility isStringValidated:aRequestModel.identifier]) {
                                                         [self.dispatchTable removeObjectForKey:aRequestModel.identifier];
                                                     }
                                                     
                                                     if (aSuccessBlock) {
                                                         aSuccessBlock(responseObject);
                                                     }
                                                 } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                                     //删除网络请求
                                                     if ([BDMUtility isStringValidated:aRequestModel.identifier]) {
                                                         [self.dispatchTable removeObjectForKey:aRequestModel.identifier];
                                                     }
                                                     
                                                     if (aFailureBlock) {
                                                         aFailureBlock([BDMError errorWithError:error]);
                                                     }
                                                 }];
    
    //记录每次的网络请求
    if ([BDMUtility isStringValidated:aRequestModel.identifier]) {
        [self.dispatchTable setObject:currentOperation forKey:aRequestModel.identifier];
    }
}

- (void)sendHttpRequestGroupWithModelArray:(NSArray<BDMHTTPRequestModel *> *)modelArray
                                  progress:(void(^)(id responseObject, NSString *identifier, BDMError *error))progress
                                  complete:(void(^)(BOOL isRequestSuccess))complete
                          isResultRestrict:(BOOL)isResultRestrict {
    
    __block int32_t value = 0;
    for (BDMHTTPRequestModel *aRequestModel in modelArray) {
        [self.groupRequestArray addObject:aRequestModel.identifier];
        
        //避免同一请求重复请求
        if ([BDMUtility isStringValidated:aRequestModel.identifier] && [self.dispatchTable objectForKey:aRequestModel.identifier] ) {
            return;
        }
        
        // 通过https的所有请求,因https请求需要证书
        self.securityPolicy.allowInvalidCertificates = !aRequestModel.isUseSSL;
        
        id parameters = aRequestModel.paraDict;
        
        if ([aRequestModel.requestMethod isEqualToString:HTTP_REQUEST_POST]) {
            if (HTTP_REQUEST_TYPE_JSON == aRequestModel.requestType) {
                parameters = aRequestModel.postDict;
                self.requestSerializer = [AFJSONRequestSerializer serializer];
            } else if (HTTP_REQUEST_TYPE_DATA_UCLOGIN == aRequestModel.requestType) {
                parameters = [aRequestModel.postDict objectForKey:HTTP_REQUEST_POST];
                self.requestSerializer = [AFUCLoginRequestSerializer serializer];
            } else if (HTTP_REQUEST_TYPE_FORM == aRequestModel.requestType) {
                self.requestSerializer = [AFHTTPRequestSerializer serializer];
                parameters = aRequestModel.postDict;
            }
        }
        else {
            self.requestSerializer = [AFJSONRequestSerializer serializer];
        }
        
        if (HTTP_RETURN_TYPE_JSON == aRequestModel.returnType) {
            self.responseSerializer = [AFJSONResponseSerializer serializer];
        } else if (HTTP_RETURN_TYPE_DATA == aRequestModel.returnType){
            self.responseSerializer = [AFHTTPResponseSerializer serializer];
        }
        
        BlockWeakObject(self, wself);
        AFHTTPRequestOperation *currentOperation = [self URL:aRequestModel.requestUrl
                                               requestMethod:aRequestModel.requestMethod
                                              customerHeader:aRequestModel.customHeader
                                                     paraDic:aRequestModel.paraDict
                                                  parameters:parameters
                                                 cachePolicy:aRequestModel.cachePolicy
                                                     success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                                         BlockStrongObject(wself, self);
                                                         //删除网络请求
                                                         if ([BDMUtility isStringValidated:aRequestModel.identifier]) {
                                                             [self.dispatchTable removeObjectForKey:aRequestModel.identifier];
                                                         }
                                                         if (progress) {
                                                             progress(responseObject, aRequestModel.identifier, nil);
                                                         }
                                                         OSAtomicIncrement32(&value);
                                                         if (value >= modelArray.count) {
                                                             if (complete) {
                                                                 complete(YES);
                                                             }
                                                         }
                                                         
                                                     } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                                         BlockStrongObject(wself, self);
                                                         //删除网络请求
                                                         if ([BDMUtility isStringValidated:aRequestModel.identifier]) {
                                                             [self.dispatchTable removeObjectForKey:aRequestModel.identifier];
                                                         }
                                                         if (progress) {
                                                             progress(nil, aRequestModel.identifier, [BDMError errorWithError:error]);
                                                         }
                                                         OSAtomicIncrement32(&value);
                                                         if (value >= modelArray.count) {
                                                             if (complete) {
                                                                 if (isResultRestrict) {
                                                                     complete(NO);
                                                                 }
                                                                 else {
                                                                     complete(YES);
                                                                 }
                                                             }
                                                         }
                                                         if (isResultRestrict) {
                                                             // 取消剩余请求，并完成回调
                                                             // 否则，一个出现错误记录即可 不需要终止其余请求
                                                             [self cancelRequestForIdentifierList:self.groupRequestArray];
                                                             if (complete) {
                                                                 complete(NO);
                                                             }
                                                         }
                                                     }];
        
        //记录每次的网络请求
        if ([BDMUtility isStringValidated:aRequestModel.identifier]) {
            [self.dispatchTable setObject:currentOperation forKey:aRequestModel.identifier];
        }
    }
}

- (void)uploadFileWithFileModel:(BDMUploadFileModel *)aUploadFileModel
               progressCallBack:(void (^)(NSUInteger __unused bytesWritten,
                                          long long totalBytesWritten,
                                          long long totalBytesExpectedToWrite))progressBlock
                        success:(void (^)(id responseObject))aSuccessBlock
                        failure:(void (^)(NSError *error))aFailureBlock
{
    //避免同一请求重复请求
    if ([BDMUtility isStringValidated:aUploadFileModel.identifier] && [self.dispatchTable objectForKey:aUploadFileModel.identifier] ) {
        return;
    }
    
    if (HTTP_RETURN_TYPE_JSON == aUploadFileModel.returnType) {
        self.responseSerializer = [AFJSONResponseSerializer serializer];
    } else if (HTTP_RETURN_TYPE_DATA == aUploadFileModel.returnType){
        self.responseSerializer = [AFHTTPResponseSerializer serializer];
    }
    
    NSError *error = nil;
    NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:aUploadFileModel.requestUrl parameters:aUploadFileModel.requestParaDict constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        if (aUploadFileModel) {
            switch (aUploadFileModel.uploadFileType) {
                case BDMUploadFileType_Data:
                {
                    [formData appendPartWithFileData:aUploadFileModel.fileData name:aUploadFileModel.name fileName:aUploadFileModel.fileName mimeType:aUploadFileModel.mimeType];
                    break;
                }
                case BDMUploadFileType_FileURL:
                {
                    [formData appendPartWithFileURL:aUploadFileModel.filePathURL name:aUploadFileModel.name fileName:aUploadFileModel.fileName mimeType:aUploadFileModel.mimeType error:nil];
                    break;
                }
                case BDMUploadFileType_InputStream:
                    break;
            }
        }
    } error:&error];

    AFHTTPRequestOperation *operation = [self httpRequestOperationWithRequest:request
                                                                      success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                                                          if ([BDMUtility isStringValidated:aUploadFileModel.identifier] && [self.dispatchTable objectForKey:aUploadFileModel.identifier] ) {
                                                                              return;
                                                                          }
                                                                          
                                                                          aSuccessBlock(responseObject);
                                                                      }
                                                                      failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                                                          
                                                                          if ([BDMUtility isStringValidated:aUploadFileModel.identifier] && [self.dispatchTable objectForKey:aUploadFileModel.identifier] ) {
                                                                              return;
                                                                          }
                                                                          
                                                                          aFailureBlock(error);
                                                                      }];
    
    [operation setUploadProgressBlock:^(NSUInteger __unused bytesWritten,
                                        long long totalBytesWritten,
                                        long long totalBytesExpectedToWrite) {
        progressBlock(bytesWritten,totalBytesWritten,totalBytesWritten);
    }];
    
    if ([BDMUtility isStringValidated:aUploadFileModel.identifier]) {
        [self.dispatchTable setObject:operation forKey:aUploadFileModel.identifier];
    }
    
    [[NSOperationQueue mainQueue] addOperation:operation];
}

- (void)downloadFileWithFileModel:(BDMDownloadFileModel *)aDownloadModel
                 progressCallBack:(void (^)(NSUInteger bytesRead,
                                            long long totalBytesRead,
                                            long long totalBytesExpectedToRead))progressBlock
                          success:(void (^)(id responseObject))aSuccessBlock
                          failure:(void (^)(NSError *error))aFailureBlock
{
    //避免同一请求重复请求
    if ([BDMUtility isStringValidated:aDownloadModel.identifier] && [self.dispatchTable objectForKey:aDownloadModel.identifier] ) {
        return;
    }
    
    NSError *error = nil;
    NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:aDownloadModel.downloadURL parameters:aDownloadModel.downloadParaDict error:&error];
    
    NSString *downloadFilePath = nil;
    if ([aDownloadModel .downloadPath respondsToSelector:@selector(containsString:)]) {
        if (![aDownloadModel.downloadPath containsString:aDownloadModel.fileName]) {
            downloadFilePath = [NSString stringWithFormat:@"%@/%@",aDownloadModel.downloadPath,aDownloadModel.fileName];
        } else {
            downloadFilePath = aDownloadModel.downloadPath;
        }
    } else {
        NSRange foundObj = [aDownloadModel.downloadPath rangeOfString:aDownloadModel.fileName options:NSCaseInsensitiveSearch];
        if (foundObj.length ==0) {
            downloadFilePath = [NSString stringWithFormat:@"%@/%@",aDownloadModel.downloadPath,aDownloadModel.fileName];
        } else {
            downloadFilePath = aDownloadModel.downloadPath;
        }
    }
    
    //检查文件是否已经下载了一部分
    unsigned long long downloadedBytes = 0;
    if (!aDownloadModel.resumeDownload) {
        if ([[NSFileManager defaultManager] fileExistsAtPath:downloadFilePath]) {
            //获取已下载的文件长度
            downloadedBytes = [self fileSizeForPath:downloadFilePath];
            if (downloadedBytes > 0) {
                NSMutableURLRequest *mutableURLRequest = [request mutableCopy];
                NSString *requestRange = [NSString stringWithFormat:@"bytes=%llu-", downloadedBytes];
                [mutableURLRequest setValue:requestRange forHTTPHeaderField:@"Range"];
                request = mutableURLRequest;
            }
        }
    }
    //不使用缓存，避免断点续传出现问题
    [[NSURLCache sharedURLCache] removeCachedResponseForRequest:request];
    //下载请求
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    //下载路径
    operation.outputStream = [NSOutputStream outputStreamToFileAtPath:downloadFilePath append:YES];
    if (aDownloadModel.SSL) {
        operation.securityPolicy = [self customSecurityPolicy:aDownloadModel.certificateName];
    }
    
    //下载进度回调
    [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
        //        //下载进度
        //        float progress = ((float)totalBytesRead + downloadedBytes) / (totalBytesExpectedToRead + downloadedBytes);
        progressBlock(bytesRead,totalBytesRead,totalBytesExpectedToRead);
    }];
    //成功和失败回调
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        if ([BDMUtility isStringValidated:aDownloadModel.identifier]) {
            [self.dispatchTable removeObjectForKey:aDownloadModel.identifier];
        }
        
        aSuccessBlock(responseObject);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        if ([BDMUtility isStringValidated:aDownloadModel.identifier]) {
            [self.dispatchTable removeObjectForKey:aDownloadModel.identifier];
        }
        
        aFailureBlock(error);
    }];
    
    if ([BDMUtility isStringValidated:aDownloadModel.identifier]) {
        [self.dispatchTable setObject:operation forKey:aDownloadModel.identifier];
    }
    
    [operation start];
}

- (void)cancelRequestForIdentifier:(NSString *)requestID
{
    NSOperation *requestOperation = self.dispatchTable[requestID];
    [requestOperation cancel];
    [self.dispatchTable removeObjectForKey:requestID];
}

- (void)cancelRequestForIdentifierList:(NSMutableArray *)requestIDList
{
    for (NSString *requestId in requestIDList) {
        [self cancelRequestForIdentifier:requestId];
    }
}

- (void)cancelAllRequest {
    [self.operationQueue cancelAllOperations];
}

#pragma mark - private method

- (AFHTTPRequestOperation *)URL:(NSString *)aURLString
                  requestMethod:(NSString *)aRequestMethod
                 customerHeader:(NSDictionary *)aCustomerHeader
                        paraDic:(id)aParaDic
                        parameters:(id)aParameters
                    cachePolicy:(NSURLRequestCachePolicy)aPolicy
                        success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))aSuccess
                        failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))aFailure
{
    NSMutableDictionary *paraDict = [[NSMutableDictionary alloc] initWithDictionary:aParaDic];
    
    NSMutableURLRequest *request;
    if ([aRequestMethod isEqualToString:HTTP_REQUEST_POST]) {
        NSMutableString * url = [[NSMutableString alloc] initWithString:aURLString];
        [url appendString:@"?"];
        [paraDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            [url appendFormat:@"%@=%@&",key, [BDMUtility urlEncoded:obj] ];
        }];
        request = [self.requestSerializer requestWithMethod:aRequestMethod URLString:[[NSURL URLWithString:url relativeToURL:self.baseURL] absoluteString] parameters:aParameters error:nil];
    } else {
        request = [self.requestSerializer requestWithMethod:aRequestMethod URLString:[[NSURL URLWithString:aURLString relativeToURL:self.baseURL] absoluteString] parameters:paraDict error:nil];
    }
    
    request.cachePolicy = aPolicy;
    
    // aParameters为dic类型才添加
    if ([aParameters isKindOfClass:[NSDictionary class]] || [aParameters isKindOfClass:[NSMutableDictionary class]]) {
        [paraDict addEntriesFromDictionary:aParameters];
    }
    
    // 按照app需求配置响应参数
    if (self.configHeaderBlock) {
        self.configHeaderBlock(request, paraDict, aCustomerHeader);
    }
    else {
        [self addHeaderForRequest:request customerHeader:aCustomerHeader];
    }
    
    AFHTTPRequestOperation *operation = [self httpRequestOperationWithRequest:request success:aSuccess failure:aFailure];
    
    //添加重定向处理
    [self setRedirectHandlerWithOperation:operation];
    
    // 添加缓存配置
    BOOL isJsonValid = [operation.responseSerializer isKindOfClass:[[AFJSONResponseSerializer serializer] class]];
    
    BlockWeakObject(self, wself);
    [operation setCacheResponseBlock:^NSCachedURLResponse *(NSURLConnection *connection, NSCachedURLResponse *cachedResponse) {
        BlockStrongObject(wself, self);
        if (self.configAdoptCachePolicyBlock) {
            return self.configAdoptCachePolicyBlock(request, cachedResponse, isJsonValid);
        }
        return nil;
    }];
    
    [self.operationQueue addOperation:operation];
    return operation;
}

/**
 *  @brief  增加ssl验证
 *
 *  @return 安全策略对象
 */
- (AFSecurityPolicy*)customSecurityPolicy:(NSString *)certificateName
{
    /**** SSL Pinning ****/
    NSString *cerPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:certificateName];
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    AFSecurityPolicy *securityPolicy = [[AFSecurityPolicy alloc] init];
    [securityPolicy setAllowInvalidCertificates:YES];
    [securityPolicy setPinnedCertificates:@[certData]];
    [securityPolicy setSSLPinningMode:AFSSLPinningModeNone];
    
    /**** SSL Pinning ****/
    
    return securityPolicy;
}

/*!
 *  添加重定向处理
 */
- (void)setRedirectHandlerWithOperation:(AFHTTPRequestOperation *)aOperation
{
    __weak typeof(aOperation) operation = aOperation;
    
    [operation setRedirectResponseBlock:^NSURLRequest *(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse) {
        /*
         *  redirectResponse 是包含了重定向之前的request的信息
         *  302是重定向
         */
        if (redirectResponse) {
            return nil;
        } else {
            return request;
        }
    }];
}

- (AFHTTPRequestOperation *)httpRequestOperationWithRequest:(NSURLRequest *)request
                                                    success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                                                    failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
    BlockWeakObject(self, wself);
    void (^SuccessBlock)(AFHTTPRequestOperation *, id) = ^(AFHTTPRequestOperation *operation, id responseObject) {
        BlockStrongObject(wself, self);
        // 根据业务需求处理成功回调后的状态码
        NSError *error;
        if (self.configResultHandlerBlock) {
            NSInteger statusCode = operation.response.statusCode;
            BOOL isJsonValid = [operation.responseSerializer isKindOfClass:[[AFJSONResponseSerializer serializer] class]];
            error =  self.configResultHandlerBlock(responseObject, isJsonValid, statusCode);
        }
        if (!error) {
            if (success) {
                success(operation, responseObject);
            }
        }
        else {
            if (failure) {
                failure(operation, error);
            }
        }
    };
    
    void (^FailureBlock)(AFHTTPRequestOperation *, NSError *) = ^(AFHTTPRequestOperation *operation, NSError *error){
        // 忽略主动取消的错误回调
        if (error.code != NSURLErrorCancelled) {
            if (failure) failure(operation, error);
        }
    };
    
    return [self HTTPRequestOperationWithRequest:request success:SuccessBlock failure:FailureBlock];
}

- (void)addHeaderForRequest:(NSMutableURLRequest *)aRequest
             customerHeader:(NSDictionary *)aCustomerHeader
{
    if ([aCustomerHeader count] != 0) {
        [aCustomerHeader enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            [aRequest setValue:obj forHTTPHeaderField:key];
        }];
    }
}

/**
 *  @brief  获取已下载的文件大小
 *
 *  @param path 文件路径
 *
 *  @return 文件大小
 */
- (unsigned long long)fileSizeForPath:(NSString *)path {
    signed long long fileSize = 0;
    NSFileManager *fileManager = [NSFileManager new];
    if ([fileManager fileExistsAtPath:path]) {
        NSError *error = nil;
        NSDictionary *fileDict = [fileManager attributesOfItemAtPath:path error:&error];
        if (!error && fileDict) {
            fileSize = [fileDict fileSize];
        }
    }
    return fileSize;
}

#pragma mark - getters

- (NSMutableDictionary *)dispatchTable
{
    if (_dispatchTable == nil) {
        _dispatchTable = [[NSMutableDictionary alloc] init];
    }
    return _dispatchTable;
}

- (NSMutableArray *)groupRequestArray {
    if (!_groupRequestArray) {
        _groupRequestArray = [[NSMutableArray alloc] init];
    }
    return _groupRequestArray;
}

@end
